﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;
using Duo1JHttpServer.Framework.Config;
using Duo1JHttpServer.Framework.Enum;
using Duo1JHttpServer.Framework.Interface;
using Duo1JHttpServer.Framework.Model;
using Duo1JHttpServer.Framework.Utils;

namespace Duo1JHttpServer.Framework.Analyzer
{
    /// <summary>
    /// 请求映射文件解析器
    /// </summary>
    internal sealed class MapperAnalyzer : IConfigAnalyzer
    {
        /// <summary>
        /// 解析Xml Mapper配置文件
        /// <param name="pathCache">路径缓存List</param>
        /// </summary>
        public void Analyze(List<string> pathCache)
        {
            List<MapperConfigModel> models = new List<MapperConfigModel>();
            foreach (string path in pathCache)
            {
                XmlDocument xml = new XmlDocument();
                xml.Load(path);
                XmlElement root = xml.DocumentElement;
                if (root != null)
                {
                    string assemblyName = null;
                    XmlAttributeCollection rootAttrs = root.Attributes;
                    foreach (XmlAttribute rootAttr in rootAttrs)
                    {
                        if (EnumUtils.GetMapperAttrType(rootAttr.Name) == MapperAttrType.Assembly)
                        {
                            assemblyName = rootAttr.Value;
                            break;
                        }
                    }

                    if (assemblyName == null)
                    {
                        ConsoleUtils.WriteRedLine($"Require assembly attribute in root node: {path}");
                        continue;
                    }

                    XmlNodeList nodeList = root.ChildNodes;
                    foreach (XmlNode node in nodeList)
                    {
                        XmlAttributeCollection attributes = node.Attributes;
                        if (attributes != null)
                        {
                            MapperConfigModel model = new MapperConfigModel();
                            model.AssemblyName = assemblyName;
                            foreach (XmlAttribute attribute in attributes)
                            {
                                MapperAttrType attrType = EnumUtils.GetMapperAttrType(attribute.Name);
                                switch (attrType)
                                {
                                    case MapperAttrType.Class:
                                        model.HandlerName = attribute.Value;
                                        break;
                                    case MapperAttrType.Url:
                                        model.Url = attribute.Value;
                                        break;
                                }
                            }

                            models.Add(model);
                        }
                    }
                }
                else
                {
                    ConsoleUtils.WriteRedLine("XmlElement Null");
                }
            }

            AnalyzeClassToObject(models);
            //放入配置类中
            ConfigManager.HandlerConfig.MapperModels = models;
        }

        /// <summary>
        /// 解析类名生成对应实例
        /// </summary>
        /// <param name="mapperModels">映射处理器模型</param>
        private void AnalyzeClassToObject(List<MapperConfigModel> mapperModels)
        {
            Assembly entryAssembly = Assembly.GetEntryAssembly();
            if (entryAssembly == null)
            {
                ConsoleUtils.WriteRedLine("Get entry assembly failed!");
                return;
            }

            foreach (MapperConfigModel model in mapperModels)
            {
                string handlerName = model.HandlerName;
                Type type = entryAssembly.GetType(handlerName);
                if (type != null)
                {
                    IHttpHandle handle = Activator.CreateInstance(type) as IHttpHandle;
                    if (handle != null)
                    {
                        model.Handle = handle;
                        ConsoleUtils.WriteTabLine($"Instantiate class success: '{handlerName}'");
                        continue;
                    }

                    try
                    {
                        Assembly attrAssembly = Assembly.Load(model.AssemblyName);
                        type = attrAssembly.GetType(handlerName);
                        if (type == null)
                        {
                            ConsoleUtils.WriteRedLine($"Can`t instantiate class: '{handlerName}'");
                            continue;
                        }

                        handle = Activator.CreateInstance(type) as IHttpHandle;
                        if (handle != null)
                        {
                            model.Handle = handle;
                            ConsoleUtils.WriteTabLine($"Instantiate class success: '{handlerName}'");
                            continue;
                        }
                    }
                    catch (FileNotFoundException)
                    {
                        ConsoleUtils.WriteRedLine($"Load assembly failed: {model.AssemblyName}");
                        throw;
                    }

                    ConsoleUtils.WriteRedLine($"Instantiate class failed: '{handlerName}'");
                    continue;
                }

                ConsoleUtils.WriteRedLine($"Can`t instantiate class: '{handlerName}'");
            }
        }
    }
}